
import sys, math
from PyQt4 import QtGui, QtOpenGL, QtCore, Qt

import picts
import graph_data

class AssetItem( QtGui.QGraphicsItemGroup ):
    def __init__(self, owner, x, y, name ):
        self.size = 50
        self.links = []
        self.name = name
        QtGui.QGraphicsItemGroup.__init__( self )
        self.setPos( x,y )
        self.setFlags(
            QtGui.QGraphicsItem.ItemIsSelectable
            | QtGui.QGraphicsItem.ItemIsMovable 
        )
        self.setToolTip("test")
        
        body = QtGui.QGraphicsEllipseItem( -self.size/2, -self.size/2, self.size, self.size, self)
        body.setFlags( QtGui.QGraphicsItem.ItemIsSelectable )
        body.setBrush( QtGui.QBrush( QtGui.QColor(92, 143, 174) ) )
        body.setPen(
            QtGui.QPen( 
                QtGui.QBrush(
                    QtGui.QColor(128, 255, 128) 
                ),
                5
            )
        )
        
        text = QtGui.QGraphicsSimpleTextItem( self.name+'\nTEST', self )
        br = text.boundingRect()
        text.setPos( -br.width()/2, self.size/2 )
        
        pix = QtGui.QGraphicsPixmapItem( QtGui.QPixmap(picts.random()), self )
        w = pix.boundingRect().width()
        pix.setPos( -w/2, -w/2 )
        
    def AddLink( self, link ):
        self.links.append( link )
    
    def GetLinkOutPoint( self ):
        return QtCore.QPointF( 0,0 )
    
    def GetLinkInPoint( self ):
        return QtCore.QPointF( -self.size/2,0 )
    
    def itemChange( self, change, value ):
        if change == QtGui.QGraphicsItem.ItemPositionHasChanged:
            [ link.Adjust() for link in self.links ]
        return QtGui.QGraphicsItemGroup.itemChange( self, change, value )
    
    def contextMenuEvent( self, event ):
        view = event.widget()
        menu = QtGui.QMenu(view)
        a = menu.addAction( self.name+" Action A")
        b = menu.addAction( self.name+" Action B")
        action = menu.exec_(QtGui.QCursor().pos())
        if action == a:
            print 'Action A'
        elif action == b:
            print 'Action B'

Pi = math.pi
TwoPi = 2.0 * Pi

class LinkItem( QtGui.QGraphicsItem ):
    def __init__( self, src, dst, ltype ):
        QtGui.QGraphicsItem.__init__( self )
        self.src = src
        self.dst = dst
        self.ltype = ltype
        if ltype == 1:
            self.color = QtGui.QColor(255, 255, 136)
        else:
            self.color = QtGui.QColor(136, 255, 255)
        self.arrowSize = 10
        src.AddLink( self )
        dst.AddLink( self )
        self.Adjust()

    def Adjust(self):
        if not self.src or not self.dst:
            return
        line = QtCore.QLineF(
            self.mapFromItem( self.src, self.src.GetLinkOutPoint() ),
            self.mapFromItem( self.dst, self.dst.GetLinkInPoint() )
        )
        length = line.length()

        self.prepareGeometryChange()

        if length > 20:
            srcEdgeOffset = QtCore.QPointF(
                ( line.dx() * self.src.size/2 ) / length, 
                ( line.dy() * self.src.size/2 ) / length
            )
            self.sourcePoint = line.p1() + srcEdgeOffset
            self.destPoint = line.p2()
        else:
            self.sourcePoint = self.destPoint = line.p1()

    def boundingRect(self):
        if not self.src or not self.dst:
            return QRectF()

        penWidth = 1;
        extra = ( penWidth + self.arrowSize ) / 2.0

        return QtCore.QRectF(
            self.sourcePoint, 
            QtCore.QSizeF( 
                self.destPoint.x() - self.sourcePoint.x(),
                self.destPoint.y() - self.sourcePoint.y()
            )
        ).normalized().adjusted( -extra, -extra, extra, extra )

    def paint( self, painter, styleOption, w ):
        if not self.src or not self.dst:
            return

        line = QtCore.QLineF( self.sourcePoint, self.destPoint )
        if QtCore.qFuzzyCompare( line.length(), 0.0 ):
            return

        color = self.destPoint.x() > self.sourcePoint.x() and self.color or QtCore.Qt.red
        # Draw the line itself
        painter.setPen(
            QtGui.QPen( 
                color, 3, QtCore.Qt.SolidLine, QtCore.Qt.RoundCap, QtCore.Qt.RoundJoin
            )
        )
        painter.drawLine(line)

        # Draw the arrows
        angle = math.acos( line.dx() / line.length() )
        if line.dy() >= 0:
            angle = TwoPi - angle

        destArrowP1 = self.destPoint + QtCore.QPointF(
            math.sin(angle-Pi/3) * self.arrowSize,
            math.cos(angle-Pi/3) * self.arrowSize
        )
        destArrowP2 = self.destPoint + QtCore.QPointF(
            math.sin(angle-Pi+Pi/3) * self.arrowSize,
            math.cos(angle - Pi + Pi / 3) * self.arrowSize
        )

        painter.setBrush(color)
        poly = QtGui.QPolygonF()
        poly.append(line.p2())
        poly.append(destArrowP1)
        poly.append(destArrowP2)
        painter.drawPolygon( poly )

class MainWindow(QtGui.QWidget): 
    def __init__(self, *args): 
        QtGui.QWidget.__init__(self, *args) 
        
        self.resize( 800,600 )
        
        # create tabs
        self.tabs = QtGui.QTabBar( self )
        self.tabs.contextMenuEvent = self.tabsContextMenuEvent
        self.scenes = []
        self.items = []
        for ctx in (1,2,3,4):
            self.tabs.addTab( 'Context #'+str(ctx) )
            scene = QtGui.QGraphicsScene()
            self.LoadGraph( scene )
            self.scenes.append( scene )
            
        # create main view
        if 0:
            self.view = QtGui.QGraphicsView( self )
            f = QtOpenGL.QGLFormat(QtOpenGL.QGL.SampleBuffers)
            print '>>>>>>>>>',  f.sampleBuffers(), f.samples()
            #self.view.setViewport( QtOpenGL.QGLWidget(QtOpenGL.QGLFormat(QtOpenGL.QGL.SampleBuffers)));
            self.view.setBackgroundBrush( QtGui.QBrush( QtGui.QColor(56, 56, 56) ) )
            self.view.wheelEvent = self.viewWheelEvent
            self.view.keyPressEvent = self.viewKeyPressEvent
            self.view.keyReleaseEvent = self.viewKeyReleaseEvent
            self.view.setFocusPolicy(QtCore.Qt.WheelFocus)
            self.view.setDragMode( QtGui.QGraphicsView.RubberBandDrag )
        else:
            import gv
            self.view = gv.GV( self )
            self.view.setFocus()
        
        # layout
        layout = QtGui.QVBoxLayout()
        layout.addWidget(self.tabs) 
        layout.addWidget(self.view) 
        self.setLayout(layout) 
        
        # connections
        self.connect( self.tabs, QtCore.SIGNAL( "currentChanged(int)"), self.OnTabChange )
        self.OnTabChange( 0 )
    
    def LoadGraph( self, scene ):
        graph = graph_data.BIG
        idToItem = {}
        for id, pos in graph[0].iteritems():
            item = AssetItem( self, pos[0], pos[1], 'Item '+str(id) )
            idToItem[id] = item
            scene.addItem(item)
        print 'Created', len( graph[0] ), 'items'
        
        for src, dst, ltype in graph[1]:
            link = LinkItem( idToItem[src], idToItem[dst], ltype )
            scene.addItem(link)
        print 'Created', len( graph[1] ), 'links'
        scene.setSceneRect( scene.sceneRect().adjusted(20,20,20,20) )
    
    def viewWheelEvent( self, event ):
        if event.delta() > 0:
            self.view.scale( 1.2, 1.2 )
        else:
            self.view.scale( 0.8, 0.8 )
        event.accept()
    
    def viewKeyPressEvent( self, event ):
        print event
        if event.key() == QtCore.Qt.Key_Alt:
            self.view.setDragMode( QtGui.QGraphicsView.ScrollHandDrag )
            self.view.setInteractive(False)
            
    def viewKeyReleaseEvent( self, event ):
        if event.key() == QtCore.Qt.Key_Alt:
            self.view.setDragMode( QtGui.QGraphicsView.RubberBandDrag )
            self.view.setInteractive(True)
    
    def tabsContextMenuEvent(self, event):
        menu = QtGui.QMenu(self)
        splitH = menu.addAction("Split H")
        splitV = menu.addAction("Split V")
        action = menu.exec_(self.mapToGlobal(event.pos()))
        if action == splitV:
            print 'SPLIT V'
        elif action == splitH:
            print 'SPLIT H'
            
    def OnTabChange( self, index ):
        self.view.setScene( self.scenes[index-1] )
        
def main(): 
    app = QtGui.QApplication(sys.argv) 
    w = MainWindow() 
    w.show() 
    sys.exit(app.exec_()) 

if __name__ == "__main__": 
    main()

